package com.nulldev.util.io.cstreams;

import java.io.Closeable;
import java.io.Flushable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;

public class CoderThread extends Thread {
	private PipedInputStream inSink;

    private PipedOutputStream outSink;

    private Runnable workhorse;

    private Throwable throwable;

    public CoderThread(final Coder coder, final InputStream in) throws IOException {
        this.inSink = new PipedInputStream();
        this.outSink = new PipedOutputStream(inSink);
        this.workhorse = new Runnable() {
            public void run() {
                try {
                    coder.code(in, outSink);
                    flush(outSink);
                }catch (Throwable e) {
                    throwable = e;
                }finally {
                    close(outSink);
                }
            }
        };
    }

    public CoderThread(final Coder coder, final OutputStream out) {
        try {
        	this.outSink = new PipedOutputStream();
            this.inSink = new PipedInputStream(outSink);
            this.workhorse = new Runnable() {
                public void run() {
                    try {
                        coder.code(inSink, out);
                        flush(out);
                    }catch (Throwable e) {
                        throwable = e;
                    }finally {
                        close(inSink);
                    }
                }
            };
        }catch (Exception e) {
        	this.outSink = null;
        	this.inSink = null;
        	e.printStackTrace();
        }
    }

    public void run() {
        workhorse.run();
    }

    // ==

    public Throwable getThrowable() {
        return throwable;
    }

    public void checkForException() throws IOException {
        if (null != throwable) {
            if (throwable instanceof IOException) {
                throw (IOException) throwable;
            }else {
                throw new IOException(throwable);
            }
        }
    }

    public PipedInputStream getInputStreamSink() {
        return inSink;
    }

    public PipedOutputStream getOutputStreamSink() {
        return outSink;
    }

    // ==

    protected boolean flush(Flushable flushable) {
        if (flushable != null) {
            try {
                flushable.flush();
                return true;
            }catch (IOException e) {
                // mute
            }
        }

        return false;
    }

    protected boolean close(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
                return true;
            }catch (IOException e) {
                // mute
            }
        }
        return false;
    }
}
